# -*- coding: utf-8 -*-
import re
import pandas as pd
import json

from JULIET import JULIET
from PERFORMANCE import PERFORMANCE

class INFER:
    '''对 infer 的 result.json 进行分析'''
    def __init__(self) -> None:
        # infer 结果 json 文件路径
        self.infer_result_json_path='../scan_data/infer/infer-scan-result.json'
        # infer 的结果与 cwe 编号对应关系的 excel 文件路径
        self.juliet_cwe_and_infer_excel_path='./resource/infer测试结果与juliet的CWE编号对应关系.xlsx'
        # infer 的结果与 cwe 编号对应关系的 excel 文件的 sheet 表名
        self.juliet_cwe_and_infer_excel_sheetname='cwe'
        # 根据 juliet_cwe_and_infer_excel_path 来生成的，将 bug_type 与 cweid 对应
        self.bug_type_to_cweid_dict={}
        # 根据 infer_result_json_path 来生成的，保存了 infer 的分析结果
        self.result=[]
        # 生成 result 数据
        self.analyze_result=[]
        # juliet 对象，里面包括了 juliet 数据集的预期数据及一些可以调用的方法
        self.juliet=JULIET()
        self.get_result_from_json_file()
        self.get_bug_type_to_cweid_dict()
        self.get_analyze_result()
        # # 性能分析
        # time_log_path='/home/pudding/work/temp/result/sast_result/sast_result/infer/3/infer_time.log'
        # top_log_path='/home/pudding/work/temp/result/sast_result/sast_result/infer/3/infer_top.log'
        # iotop_log_path='/home/pudding/work/temp/result/sast_result/sast_result/infer/3/infer_iotop.log'
        # top_command='infer'
        # iotop_command='infer run --bufferoverrun --biabduction --pulse -- make -j6 -B'
        # performance_save_path='infer_performance.xlsx'
        # performance=PERFORMANCE(time_log_path,top_log_path,iotop_log_path,top_command,iotop_command,performance_save_path)

    def get_result_from_json_file(self):
        '''将 infer 分析出的 json 结果解析到 result 对象中'''
        with open(self.infer_result_json_path,'r') as f:
            infer_json_file_content=json.loads(f.read())
        for item in infer_json_file_content:
            one_result={
                'bug_type':item['bug_type'],
                'line':str(item['line']),
                'procedure':item['procedure'],
                'file':item['file']
            }
            self.result.append(one_result)
    
    def get_bug_type_to_cweid_dict(self):
        '''将 bug_type 字段解析为 cweid'''
        df=pd.read_excel(self.juliet_cwe_and_infer_excel_path,sheet_name=self.juliet_cwe_and_infer_excel_sheetname)
        for i in range(len(df['cweid'])):
            cweid=df['cweid'][i]
            bug_types=df['bug_types'][i]
            if pd.isnull(bug_types):
                continue
            for bug_type in bug_types.split(' '):
                if not self.bug_type_to_cweid_dict.get(bug_type):
                    self.bug_type_to_cweid_dict[bug_type]=[cweid]
                else:
                    self.bug_type_to_cweid_dict[bug_type].append(cweid)

    def get_analyze_result(self):
        '''对结果进行分析'''
        # juliet 测试用例的总数
        juliet_testcases_number=0
        for item in self.juliet.result.values():
            juliet_testcases_number+=len(item)
        # infer 重复报告相同的 juliet 测试用例数量
        infer_repeat_number=0
        # infer 的结果总数
        infer_result_number=len(self.result)
        # 匹配成功的用例数量
        success_report_number=0
        # 匹配成功的 juliet 的测试用例列表,避免同一个测试用例被多次算作 success_report_number 里
        success_juliet_testcase_list=[]
        # 初始化 cweid_success_number_dict，用于记录各 cwe 编号成功测试用例数
        # for item in self.juliet.result.keys():
        #     self.cweid_success_number_dict[item]=0
        #     self.cweid_error_number_dict[item]=0
        # 遍历 infer 的 result
        for item in self.result:
            # 如果是与 juliet 测试用例中的 cwe 编号匹配的 bug_type
            if item['bug_type'] in self.bug_type_to_cweid_dict.keys():
                cweid_list=self.bug_type_to_cweid_dict[item['bug_type']]
                file_name=item['file'].split('/')[-1]
                # 根据文件名对应到 juliet 里的一个测试用例
                juliet_testcase=self.juliet.get_testcase_by_file_name(file_name)
                # 如果找不到该文件名
                if not juliet_testcase:
                    continue
                juliet_testcase_cweid=self.juliet.get_testcase_cweid(juliet_testcase)
                # 如果 juliet 测试用例的 cweid 在 bug_type 对应的 cweid_list 中
                if juliet_testcase_cweid in cweid_list:
                    # 如果行数匹配,判断为一次成功报告,并记录到 success_juliet_testcase_list 中防止后续被重复计算
                    if self.juliet.if_mate_line(item['line'],file_name,juliet_testcase):
                        # 判断该 juliet 的测试用例如果之前有没有被统计过
                        if juliet_testcase not in success_juliet_testcase_list:
                            success_report_number+=1
                            success_juliet_testcase_list.append(juliet_testcase)
                            continue
                        else:
                            infer_repeat_number+=1
                    # 行数不匹配的情况下,判断当前报告的函数是否为 bad 函数
                    elif self.juliet.if_bad_function_name(item['procedure']):
                        # 判断该 juliet 的测试用例如果之前有没有被统计过
                        if juliet_testcase not in success_juliet_testcase_list:
                            success_report_number+=1
                            success_juliet_testcase_list.append(juliet_testcase)
                            continue
                        else:
                            infer_repeat_number+=1

        # 召回率/准确率：正确报告数与测试用例中用例总数的比值
        rcr=success_report_number/juliet_testcases_number
        # 误报率：错误报告数占工具所有报告总数的比值，错误报告数=infer总报告数-infer成功报告数-infer重复报告数
        fpr=(infer_result_number-success_report_number-infer_repeat_number)/infer_result_number
        excel_res={
            'juliet_testcases_number':[juliet_testcases_number],
            'infer_result_number':[infer_result_number],
            'success_report_number':[success_report_number],
            'rcr':[rcr],
            'fpr':[fpr],
        }
        df=pd.DataFrame(excel_res)
        df.to_excel('./analyze_result/infer_analyze_result.xlsx', sheet_name='main')